package com.smy.common;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.*;

public class MyDaoImpl<T> implements MyDao<T> {
    /**
     * 添加
     * @param t 传入添加的对象
     * @param c 链接
     * @return 添加成功后返回主键, 失败返回0
     */
    @Override
    public int insert(T t,Connection c) {
        int id = 0;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            StringBuilder sql = new StringBuilder();
            sql.append("insert into ");
            //前缀
            sql.append(BaseDao.tableFix);
            //反射
            Class<?> tClz = GenericsUtils.getSuperClassGenricType(this.getClass());
            //拼接表名
            sql.append(SqlHelper.getName(tClz.getSimpleName()));
            //拼接字段
            sql.append(" ( ");
            Field[] declaredFields = tClz.getDeclaredFields();
            sql.append(SqlHelper.getFields(declaredFields));
            sql.append(" ) ");
            sql.append(" values ");
            sql.append(" ( ");
            for (int i = 0; i < declaredFields.length; i++) {
                sql.append("?");
                if (i!=declaredFields.length-1)sql.append(",");
            }
            sql.append(" ); ");
            ps = c.prepareStatement(sql.toString(),Statement.RETURN_GENERATED_KEYS);
            //赋值
            //反射获取get方法
            setObject(t, declaredFields, ps);
            ps.executeUpdate();
            rs = ps.getGeneratedKeys();
            //拿到主键的get方法
            Field primaryKeyField = declaredFields[0];
            if (rs.next()) tClz.getDeclaredMethod("set" + (char) (primaryKeyField.getName().charAt(0) - 32) + primaryKeyField.getName().substring(1),primaryKeyField.getType()).invoke(t,id = rs.getInt(1));
            return id;
        } catch (SQLException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            BaseDao.close(null,ps,rs);
        }
    }

    /**
     * 多添加
     * @param batch 要添加的数据集合
     * @param c 连接
     * @return 所有主键
     */
    @Override
    public List<Integer> insertBatch(List<T> batch, Connection c) {
        return null;
    }

    /**
     * 根据id查询
     * @param id id
     * @param c 连接
     * @return 查询到的对象
     */
    @Override
    public T selectById(int id, Connection c) {
        T t = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            StringBuilder sql = new StringBuilder();
            sql.append("select  ");
            //获取泛型类
            Class<?> tClz = GenericsUtils.getSuperClassGenricType(this.getClass());
            //所有字段
            Field[] declaredFields = tClz.getDeclaredFields();
            sql.append(SqlHelper.getFields(declaredFields));
            //前缀
            sql.append(" from ");
            sql.append(BaseDao.tableFix);
            //拼接表名
            sql.append(SqlHelper.getName(tClz.getSimpleName()));
            sql.append(" where ");
            sql.append(SqlHelper.getName(declaredFields[0].getName()));
            sql.append(" = ? ");
            ps = c.prepareStatement(sql.toString());
            //设置主键
            ps.setObject(1,id);
            //解析返回
            rs = ps.executeQuery();
            if (rs.next()) {
                //每一个数据创建一个T实例
                t = (T) tClz.getDeclaredConstructor().newInstance();
                //获取全部Set方法
                for (int i = 0; i < declaredFields.length; i++) {
                    //获取set方法并执行
                    tClz.getDeclaredMethod("set" + (char) (declaredFields[i].getName().charAt(0) - 32) + declaredFields[i].getName().substring(1),declaredFields[i].getType()).invoke(t,rs.getObject(i+1));
                }
            }
            return t;
        } catch (SQLException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            BaseDao.close(null,ps,rs);
        }
    }

    /**
     * 查询全部
     * @param c 连接
     * @return 查询到的所有结果封装成的List集合
     */
    @Override
    public List<T> selectAll(Connection c) {
        List<T> result = new ArrayList<>();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            StringBuilder sql = new StringBuilder();
            sql.append("select  ");
            //获取泛型类
            Class<?> tClz = GenericsUtils.getSuperClassGenricType(this.getClass());
            //所有字段
            Field[] declaredFields = tClz.getDeclaredFields();
            sql.append(SqlHelper.getFields(declaredFields));
            //前缀
            sql.append(" from ");
            sql.append(BaseDao.tableFix);
            //拼接表名
            sql.append(SqlHelper.getName(tClz.getSimpleName()));
            ps = c.prepareStatement(sql.toString());
            rs = ps.executeQuery();
            encapsulation(rs, tClz, declaredFields, result);
            return result;
        } catch (SQLException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            BaseDao.close(null,ps,rs);
        }
    }

    /**
     * 根据传入的t进行修改
     * @param t 传入的t
     * @param c 连接
     * @return 如果顺利返回t的id , 失败返回0
     */
    @Override
    public int updateById(T t, Connection c) {
        int id = 0;
        PreparedStatement ps = null;
        try {
            StringBuilder sql = new StringBuilder();
            sql.append("update ");
            //前缀
            sql.append(BaseDao.tableFix);
            //反射
            Class<?> tClz = GenericsUtils.getSuperClassGenricType(this.getClass());
            //拼接表名
            sql.append(SqlHelper.getName(tClz.getSimpleName()));
            //拼接字段
            sql.append(" set ");
            //获取所有字段
            Field[] declaredFields = tClz.getDeclaredFields();
            //提取主键字段
            Field primaryKeyField = declaredFields[0];
            //拿到没有主键的所有字段
            Field[] noPrimaryKeyFields = new Field[declaredFields.length-1];
            System.arraycopy(declaredFields, 1, noPrimaryKeyFields, 0, declaredFields.length - 1);
            //添加到条件中
            sql.append(SqlHelper.getFieldsByUpdate(noPrimaryKeyFields));
            //根据主键修改
            sql.append(" where ");
            sql.append(SqlHelper.getName(primaryKeyField.getName()));
            sql.append(" = ? ");
            ps = c.prepareStatement(sql.toString());
            //赋值
            //反射获取除了主键之外的所有get方法
            setObject(t, noPrimaryKeyFields, ps);
            //反射获取主键get方法执行后赋值
            int primaryKey = (int)tClz.getDeclaredMethod("get" + (char) (primaryKeyField.getName().charAt(0) - 32) + primaryKeyField.getName().substring(1)).invoke(t);
            //赋值where语句占位符
            ps.setObject(declaredFields.length,primaryKey);
            //执行
            ps.executeUpdate();
            //赋值返回id
            id = primaryKey;
        } catch (SQLException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            BaseDao.close(null,ps,null);
        }
        return id;
    }

    /**
     *
     * @param t 传入的对象
     * @param declaredFields 要操作的字段数组
     * @param ps 要赋值的防注入执行体
     */
    private void setObject(T t, Field[] declaredFields, PreparedStatement ps) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLException {
        for (int i = 0; i < declaredFields.length; i++) {
            Field declaredField = declaredFields[i];
            String name = declaredField.getName();
            String s = "get" + (char) (name.charAt(0) - 32) + name.substring(1);
            Object invoke = GenericsUtils.getSuperClassGenricType(this.getClass()).getDeclaredMethod(s).invoke(t);
            ps.setObject(i + 1, invoke);
        }
    }

    /**
     * 复合查询
     * @param bool 查询对象
     * @param c 连接
     * @return 数据被赋值后的查询对象
     */
    @Override
    public Bool<T> boolQuery(Bool<T> bool, Connection c){
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            StringBuilder sql = new StringBuilder();
            sql.append("select  ");
            //获取泛型类
            Class<?> tClz = GenericsUtils.getSuperClassGenricType(this.getClass());
            Field[] declaredFields;
            //指定字段
            if(bool.getQueryFields()!=null && bool.getQueryFields().size()!=0) {
                List<String> queryFields = bool.getQueryFields();
                declaredFields = new Field[queryFields.size()];
                for (int i = 0; i < queryFields.size(); i++) {
                    Field field = tClz.getDeclaredField(queryFields.get(i));
                    declaredFields[i] = field;
                }
            }
            //所有字段
            else declaredFields = tClz.getDeclaredFields();
            //拼接要查询的字段
            sql.append(SqlHelper.getFields(declaredFields));
            sql.append(" from ");
            //拼接前缀
            sql.append(BaseDao.tableFix);
            //拼接表名
            sql.append(SqlHelper.getName(tClz.getSimpleName()));
            //条件查询
            if (bool.getCondition()!=null&& bool.getCondition().length()!=0){
                sql.append(" where 1 = 1 ");
                sql.append(bool.getCondition());
            }

            //排序
            if (bool.getSort()!=null&& bool.getSort().size()!=0){
                sql.append(" Order by ");
                Map<String, Boolean> sort = bool.getSort();
                Set<String> strings = sort.keySet();//所有指定排序字段
                boolean isAppendComma = false;
                for (String string : strings) {
                    if (isAppendComma) sql.append(" , ");
                    else {
                        Boolean isAsc = sort.get(string);
                        string = SqlHelper.getName(string);//转换成表中真实的字段名称
                        ////拼接排序
                        sql.append(string);
                        if (!isAsc) sql.append( " desc ");
                        else sql.append(" asc ");
                        isAppendComma = true;
                    }
                }
            }
            List<Object> assignment = bool.getAssignment();
            //分页
            if (bool.getCurrent()!=null && bool.getPageSize() !=null){
                sql.append(" limit ? , ? ");
                assignment.add((bool.getCurrent() -1)* bool.getPageSize());
                assignment.add(bool.getPageSize());
            }
            ps = c.prepareStatement(sql.toString());
            for (int i = 0; i < assignment.size(); i++) ps.setObject(i+1,assignment.get(i));
            rs = ps.executeQuery();
            //定义返回数据
            List<T> result = new ArrayList<>();
            //对返回数据进行封装
            encapsulation(rs, tClz, declaredFields, result);
            //设置总条数
            bool.setTotal(selectTotal(c, bool));
            //设置返回的数据
            bool.setData(result);
        } catch (SQLException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            BaseDao.close(null,ps,rs);
        }
        return bool;
    }

    /**
     * 封装数据
     * @param rs 查询返回结果集对象
     * @param tClz 反射获取的Class对象
     * @param declaredFields 要封装的所有字段
     * @param result page对象返回的data集合
     */
    private void encapsulation(ResultSet rs, Class<?> tClz, Field[] declaredFields, List<T> result) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        T t;
        while (rs.next()) {
            //每一个数据创建一个T实例
            t = (T) tClz.getDeclaredConstructor().newInstance();
            //获取全部Set方法
            for (int i = 0; i < declaredFields.length; i++) {
                String name = declaredFields[i].getName();
                Class<?> type = declaredFields[i].getType();
                String s = "set" + (char) (name.charAt(0) - 32) + name.substring(1);
                //获取set方法并执行
                tClz.getDeclaredMethod(s,type).invoke(t,rs.getObject(i+1));
            }
            result.add(t);
        }
    }

    @Override
    public int deleteById(T t,Connection c)  {
        int id = 0;
        PreparedStatement ps = null;
        try {
            StringBuilder sql = new StringBuilder();
            sql.append("delete from ");
            //前缀
            sql.append(BaseDao.tableFix);
            //反射
            Class<?> tClz = GenericsUtils.getSuperClassGenricType(this.getClass());
            //拼接表名
            sql.append(SqlHelper.getName(tClz.getSimpleName()));
            //获取所有字段
            Field[] declaredFields = tClz.getDeclaredFields();
            //提取主键字段
            Field primaryKeyField = declaredFields[0];
            //根据主键修改
            sql.append(" where ");
            sql.append(SqlHelper.getName(primaryKeyField.getName()));
            sql.append(" = ? ");
            ps = c.prepareStatement(sql.toString());
            //赋值
            //反射获取主键get方法执行后赋值
            int primaryKey = (int)tClz.getDeclaredMethod("get" + (char) (primaryKeyField.getName().charAt(0) - 32) + primaryKeyField.getName().substring(1)).invoke(t);
            //赋值where语句占位符
            ps.setObject(1,primaryKey);
            //执行
            ps.executeUpdate();
            //赋值返回id
            id = primaryKey;
        } catch (SQLException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            BaseDao.close(null,ps,null);
        }
        return id;
    }

    private Integer selectTotal (Connection c, Bool<T> bool) {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //sql
            StringBuilder sql = new StringBuilder();
            sql.append("select count(1) from   ");
            //前缀
            sql.append(BaseDao.tableFix);
            //反射
            Class<?> tClz = GenericsUtils.getSuperClassGenricType(this.getClass());
            //拼接表名
            sql.append(SqlHelper.getName(tClz.getSimpleName()));
            //设置条件
            if (bool.getCondition()!=null&& bool.getCondition().length()!=0){
                sql.append(" where 1 = 1 ");
                sql.append(bool.getCondition());
            }
            ps = c.prepareStatement(sql.toString());
            if (bool.getCurrent()!=null&&bool.getPageSize()!=null)for (int i = 0; i < bool.getAssignment().size()-2; i++) ps.setObject(i + 1, bool.getAssignment().get(i));
            else for (int i = 0; i < bool.getAssignment().size(); i++) ps.setObject(i + 1, bool.getAssignment().get(i));
            rs = ps.executeQuery();
            if (rs.next()) return rs.getInt(1);
            else return null;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            BaseDao.close(null, ps, rs);
        }
    }
}
